home *** CD-ROM | disk | FTP | other *** search
- {
- PART 2 OF NEWGRAPH.PAS
-
- You need NEWGRPH1.PAS first. Insert this into the bottom of the
- NEWGRPH1.PAS file, compile and use this TPU till yer blue in the
- face!!!
-
-
-
- ****************
- Palette routines
- ****************
-
- }
-
-
-
-
- {
- Get the red, green and blue components of a colour.
-
- Expects : ColourNumber is the number of the colour of which you
- want to read the Palette values (0-255).
- RedValue, GreenValue, BlueValue need not be initialised.
-
- Returns : The Red, Green, Blue Values of the colour specified
- by ColourNumber.
-
- }
-
- Procedure GetPalette(ColourNumber : Byte;
- VAR RedValue, GreenValue, BlueValue : Byte); Assembler;
- Asm
- MOV DX,$3C7 { $3C7 is colour ** READ ** select port. }
- MOV AL,ColourNumber { Select colour to read }
- OUT DX,AL
- ADD DL,2 { DX now = $3C9, which must be read 3 times
- in order to obtain the Red, Green and
- Blue values of a colour }
-
- IN AL,DX { Read red amount. Don't use IN AX,DX as
- for some strange reason it doesn't work ! }
- LES DI,RedValue
- MOV [ES:DI],AL { Techie saddos note : STOSB is approx 4 cycles
- slower and requires double cache multiplex,
- which basically means "who gives a shit ?". :-)
- }
-
- IN AL,DX
- LES DI,GreenValue
- MOV [ES:DI],AL
-
- IN AL,DX { Read blue }
- LES DI,BlueValue
- MOV [ES:DI],AL
- End;
-
-
-
-
-
- {
- This will change the red green and blue components of a colour,
- thereby affecting it's shade. How's that for picturesque speech ?
-
- Note : You don't need a PaletteType record to use this command,
- it affects the screen directly.
-
- Expects : ColourNumber is the number of the colour from 0 to 255.
- RedValue is the red component of the colour (0-63).
- GreenValue is the green component of the colour (0-63).
- BlueValue is the blue component of the colour (0-63).
-
- Returns : Nothing
-
- Corrupts : AL,DX
- }
-
-
- Procedure SetPalette(ColourNumber, RedValue, GreenValue, BlueValue : Byte); Assembler;
- Asm
- MOV AL,ColourNumber
- MOV DX,$3c8 { Write to Port $3C8 with number of Colour to alter }
- OUT DX,AL
- INC DL { $3C9 again ! }
- MOV AL,RedValue { Store Red }
- OUT DX,AL
- MOV AL,GreenValue { Store Green }
- OUT DX,AL
- MOV AL,BlueValue { Store Blue }
- OUT DX,AL
- End;
-
-
-
-
-
-
- (*
- This Procedure takes a snapshot of all of the colours on screen.
-
- Expects : Palette is a variable of type PaletteType which will
- hold the 256 colour palette.
-
- Returns : Nothing
-
- Notes : Use this command just before a mode change so that
- you can restore the palette to it's original state
- (via SetAllPalette) at the end of the program.
- (Unless of course you want to corrupt everything)
-
- *)
-
-
- Procedure GetAllPalette(Var Palette : PaletteType);
- Var ColourCount:byte;
- Begin
- For ColourCount:=0 to (MaxColours - 1) do
- GetPalette(ColourCount,Palette.RedLevel[ColourCount],
- Palette.GreenLevel[ColourCount],Palette.BlueLevel[ColourCount]);
- End;
-
-
-
-
-
-
-
- {
- Do I need to explain what this does? It loads in a Palette
- from file FileName and stores it in the variable Palette.
- Easy enough to use.
-
- Expects : FileName is standard MS-DOS filename which refers to the
- palette file.
- Palette is variable of type PaletteType used to hold
- the palette data.
-
- Corrupts : Don't know.
- }
-
- Procedure LoadPalette(FileName: String; Var Palette : PaletteType);
- Var PaletteFile: File;
- Begin
- Assign(PaletteFile,FileName);
- Reset(PaletteFile,1);
- BlockRead(PaletteFile,Palette,SizeOf(Palette));
- Close(PaletteFile);
- End;
-
-
-
-
-
- {
- Guess what this does then !.
-
- Expects : FileName is the MS-DOS file spec of the palette to be saved.
- Palette is the palette to be saved.
-
- Returns : Nothing
-
- Corrupts : As it's not in assembler it's hard to say, but I guess
- Pascal preserves all registers on entry to a routine ..
- (Don't quote me on that !)
- }
-
- Procedure SavePalette(FileName: String; Palette : PaletteType);
- Var PaletteFile: File;
- Begin
- Assign(PaletteFile,FileName);
- Rewrite(PaletteFile,1);
- BlockWrite(PaletteFile,Palette,SizeOf(Palette));
- Close(PaletteFile);
- End;
-
-
-
-
-
-
-
- {
- This sets the DACs to the Colours specified in your
- Palette array. Do NOT alter the Palette data structure
- or else this won't work.
-
- Expects : Palette is an initialised palette of PaletteType.
-
- Returns : Nothing
-
- Corrupts : AL, BX, CL, DX, SI, DI, ES
- }
-
-
- Procedure SetAllPalette(Palette : PaletteType); Assembler;
- Asm
- PUSH DS
- LDS BX, Palette { DS:BX points to Palette record }
- XOR AL,AL
- MOV DX,$3c8 { $3c8 selects the first colour to alter.
- After 3 writes to $3c9, the VGA automatically
- moves to the next Colour so there is no
- need to write to $3c8 again. }
- OUT DX,AL
- INC DL { Make DX = $3c9, which is used to set the
- Red / Green and Blue values of a Colour }
-
- MOV CL,(MaxColours-1) { 256 colours }
-
- MOV SI,BX
- ADD SI,MaxColours { Make SI point to green levels }
- MOV DI,BX
- ADD DI,MaxColours { Make DI point to blue levels }
- ADD DI,MaxColours
-
- {
- Note: I read somewhere that some VGA adapters don't like
- being hit with continuous data too quickly..
-
- If not then you should use the BIOS load palette
- function (which will be 20 times slower than this
- hack trick)
- }
-
- @WritePaletteInfo:
- MOV AL, [BX] { Read red level from Palette struct }
- OUT DX,AL { Write to port $3c9 }
- MOV AL, [SI] { Read green level from Palette struct }
- OUT DX,AL { Write to port $3c9 }
- MOV AL, [DI] { Read blue level from Palette struct }
- OUT DX,AL { Write to port $3c9 }
-
- INC DI { Next Red part of record }
- INC BX { Next Green }
- INC SI { Next Blue }
-
- DEC CL
- CMP CL,$FF { Dunno if a JNZ works when register is 0
- or $ff. }
- JNZ @WritePaletteInfo
- POP DS
- End;
-
-
-
-
-
-
- {
- Set the new graphics colour. Also affects text routines as well.
-
- Expects : NewColour is the new graphics Colour.
-
- Returns : Nothing.
-
- Corrupts : AL.
- }
-
-
- Procedure SetColour(NewColour:byte); Assembler;
- Asm
- MOV AL,NewColour
- MOV CurrentColour,AL
- End;
-
-
-
-
-
-
- {
- Get the current graphics colour.
- }
-
- Function GetColour: byte; Assembler;
- Asm
- MOV AL, CurrentColour;
- End;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- ****************
- Sprite Functions
- ****************
- }
-
-
- {
- Get the width of a shape
-
- Expects : DataPtr^ points to a shape in memory
-
- Returns : Width of shape (1-255)
-
- Corrupts : ES, DI
- }
-
- Function ShapeWidth(Var DataPtr): byte; assembler;
- Asm
- LES DI,DataPtr
- MOV AL,[ES:DI]
- End;
-
-
-
-
- {
- Get the height (in pixels) of an Shape.
-
- Expects : DataPtr^ points to a shape held in memory
-
- Returns : Height of shape (1-255)
-
- Corrupts : ES,DI
- }
-
- Function ShapeHeight(Var DataPtr): byte; assembler;
- Asm
- LES DI,DataPtr
- MOV AL,[ES:DI+1]
- End;
-
-
-
-
-
-
-
- {
- This Function returns the number of bytes required to store
- a shape object of a given width and height.
-
- Expects : ShapeWidth is the width of the Shape (1-255). You can
- obtain the width of a shape by using the ShapeWidth
- Function above.
-
- Shapeheight is the height of the Shape (1-255). You can
- obtain the height of a shape by using the ShapeHeight
- Function above.
-
- Returns : ExtShapeSize = No of bytes shape uses.
-
-
- Corrupts : AL,BL.
-
- }
-
-
- Function ExtShapeSize(ShapeWidth, ShapeHeight : byte): word; Assembler;
- Asm
- MOV AL, ShapeWidth
- MOV BL, ShapeHeight
- MUL BL
- INC AX
- INC AX
- End;
-
-
-
-
- {
- Calculate the number of bytes required to hold a shape in memory,
- if grabbed from the screen.
-
- Expects : X1, Y1, X2, and Y2 define a rectangular region that
- lies on an imaginary screen (No reading of source/
- dest Bitmap is done!). X1 and X2 must be in the range of
- 0-319; Y1 and Y2 must be in the range of 0-199.
-
- You are restricted to images up to 255 x 200 pixels
- in size. (Why 200? Well, you can't grab past the
- vertical limits of the VGA screen can you ?)
-
- Returns : Number of bytes used to hold image. If 0, then this
- means the image is too large to load into a 64K
- portion of RAM.
-
- Corrupts : BX,DX.
- }
-
- Function ShapeSize(x1,y1,x2,y2:word):word; Assembler;
- Asm
- MOV AX,x2 { Width = (X2 - X1) + 1 }
- SUB AX,x1
- INC AX { Add one extra width byte }
- AND AH,$7F
- OR AH,AH
- JNZ @TooBig
-
- MOV BX,y2 { Height = (Y2 - Y1) + 1 }
- SUB BX,y1
- INC BX
- AND BH,$7F { And again }
- CMP BX,201
- JB @ShapeFine { No, shape is OK in width and height }
-
- @TooBig:
- XOR AX,AX { Set AX to return 0, meaning error }
- JMP @Finished
-
- @ShapeFine:
- MUL BL { SpriteDataSize = Width * Height }
- ADD AX,2 { Take into account 2 bytes for Shape "header" }
-
- @Finished:
- End;
-
-
-
-
-
-
-
-
-
-
-
- {
- Display a shape at a given position on screen, over the current
- background (Most games with sprites use this technique). And
- if this isn't the fastest sprite routine in the SWAG then I'll
- eat my C64.
-
- Expects : X and Y specify a horizontal and vertical position for
- the TOP LEFT of an Shape. (Regardless whether or not the
- shape's edge is transparent)
-
- X and Y are presumed ALWAYS valid : i.e. Within bounds of
- screen; Also, it is presumed that the sprite is not placed
- in a position on screen that over runs the screen borders:
- unexpected effects would occur. Sorry! Use ClipBlit if you
- must place sprites in the screen border.
-
- DataPtr, the untyped variable, must point to data for a
- sprite which is up to 254 pixels wide and 200 pixels
- tall.
-
- Returns : Nothing
-
- Corrupts : AX,BX,CX,DX,SI,DI,ES, & Direction Flag
-
- }
-
- Procedure Blit(x,y:word; Var DataPtr); Assembler; { A - Ha ! }
- Asm
- MOV AX,x
- MOV BX,y
- CALL CalculateOffset { Calculate where to blit to }
-
- MOV ES,SourceBitmapSegment { Point ES to source Bitmap }
-
- MOV CX,DS { Faster than stack }
- LDS SI,DataPtr { resides in memory. }
-
- MOV DX,[SI] { Get Width into DL and height to DH }
- INC SI { Faster than ADD SI,2 - I think }
- INC SI
- CLD { Make sure writes are descending }
-
- MOV AH,DL { Save width in CL }
-
- @Outer:
- MOV DL,AH { Reload DL }
- MOV DI,BX { DI = Where to write to }
-
- { You could use a LODSD, but to be honest it's more trouble than
- it's worth writing the tons of extra code just to save an extra
- clock cycle or two. That is, if it does..
- }
-
-
- @Main:
- LODSB { Read byte from DS:SI }
- OR AL,AL { Is it value 0, meaning transparent ? }
- JZ @NoBlit { Yes, so ignore byte }
- MOV [ES:DI],AL { Otherwise write it to the screen. Don't
- use STOSB ! }
-
- @NoBlit:
- INC DI
- DEC DL { Reduce horizontal counter }
- JNZ @Main { If not zero then do next byte of the
- sprite column }
-
- @NextScanLine:
- ADD BX,320 { Move down 1 scan line }
- DEC DH { Reduce vertical count }
- JNZ @Outer { If not all lines of sprite done back to @Outer }
- MOV DS,CX { Restore Data Segment }
- End;
-
-
-
-
-
-
-
-
- {
- This routine writes a shape to the source Bitmap with no Colour 0
- transparency, totally overwriting everything "beneath" it.
- Also, there is no clipping of Shape. (Use ClipBlock for this
- purpose)
-
- Expects : X and Y specify the horizontal and vertical coordinate
- of the Shape pointed to by DataPtr.
-
- Returns : Nothing
-
- Corrupts : AX,BX,CL,DX,SI,DI,ES
-
- Notes : Block is especially useful for "tile" based maps.
- }
-
- Procedure Block(x,y:word; Var DataPtr); Assembler;
- Asm
- MOV AX,x
- MOV BX,y
- CALL CalculateOffset
- CMP BX,-1 { Off screen ? }
- JZ @StupidUser
-
- PUSH DS { Save DS on stack }
- MOV ES,SourceBitmapSegment { ES: BX -> Where sprite written to }
-
- CLD { Make sure writes are descending }
- LDS SI,DataPtr { This has to be last access of memory
- variable as DS is now altered }
- MOV DX,[SI] { Get width into DL, height into DH }
- ADD SI,2 { SI now points to sprite data }
-
-
- @Outer:
- MOV DI,BX { DI = Offset into VGA screen }
- MOV CL,DL { CL = Width of sprite }
-
- CMP CL,4 { Bytes left < 4 ? }
- JB @CantDoLongWordBlit { Yeah, so can't do the 4 byte blit }
-
- SHR CL,2 { Divide Bytes left by 4 }
-
- @CopyLong:
- DB $66 { Otherwise, store longword to [ES:DI] ! }
- MOVSW
- DEC CL { CL is long word count }
- JNZ @CopyLong { If CL <> 0 go back to CopyLong }
-
- MOV CL,DL { Restore CL }
- AND CL,3
- OR CL,CL
- JZ @NoBytesLeft
-
- @CantDoLongWordBlit:
- CMP CL,2 { Byte count < 2 ? }
- JB @DoByteBlit { Yes, can't do a word blit (Shit !)
- so that means that there's only
- 1 byte left. }
-
- @CopyWord:
- MOVSW { Otherwise, write word }
-
- @DoByteBlit:
- TEST CL,1 { Is there a byte left ? }
- JZ @NoBytesLeft { No, so no more blits this line }
-
- MOVSB { Store the last byte }
-
- @NoBytesLeft:
- ADD BX,320 { Advance BX to next scan line }
- DEC DH { Reduce Y count }
- JNZ @Outer { if <>0 then go to Outer }
- POP DS { Otherwise, restore Data Segment }
-
- @StupidUser:
- End;
-
-
-
-
-
-
-
- {
- Perform clipping calculations on an object.
-
- Expects : AX to be an X coordinate for a sprite
- BX to be a Y coordinate
- ES:DI to point to the sprite data
-
- Returns : If no draw can be done, carry is set TRUE.
- Else carry is FALSE and :
-
- SI will point to first byte to blit
- DI will be the VGA screen offset for first blit
- (ES still is at sprite segment however so must
- be changed afterwards)
- CL is the number of bytes to blit ACROSS
- CH is the number of bytes to blit DOWN
- DX is the MODULO for the image (i.e. how many bytes SI should
- skip (after reload) to get to the start of next row of
- sprite data)
-
- Notes : Unless you are planning to write extra routines which may
- clip images up to 256 x 256 it is wise to leave this Procedure
- as private to the unit as it is quite complex.
- }
-
-
- Procedure ClipCalculations; Near; Assembler;
- Asm
- CMP BX,199 { Y > 199 ? }
- JG @NoDraw { JG is for SIGNED integers. If Y pos is
- > 199 then no blit }
-
- CMP AX,319 { X > 319 ? }
- JG @NoDraw { Yes, Do not do any blits at all }
-
- MOV SI,DI
- INC SI
- INC SI { Make SI point to actual sprite data }
-
- XOR CH,CH
- MOV CL,[ES:DI] { CL holds Clipwidth }
-
- CMP AH,$80 { Quick test if X position is negative }
- JB @XNotNegative { If not then check if image is off right hand
- of screen }
- NEG AX { Make X position positive }
-
- CMP AX,CX { If Abs(X) >= Image Width Then Don't Draw }
- JA @NoDraw
-
- SUB CX,AX { Dec(ClipWidth, Abs(X)) }
- ADD SI,AX { Inc(DataStart, Abs(X)) }
- XOR AX,AX { Set X to 0 }
- JMP @NowDoY { Do Y portion of data now. }
-
-
- @XNotNegative:
- MOV DX,CX { Set DX to clipwidth }
- ADD DX,AX { If X + ClipWidth < 320 Then }
- CMP DX,320
- JB @NowDoY { Do Y part (No need to clip width) }
- MOV CX,320
- SUB CX,AX { ClipWidth = 320 - X }
-
-
- {
- At this point:
-
- AX is the X position of the Shape
- BX is the Y position of the Shape
- CL is the clipped width of the Shape.
-
- Now it is time to do the height part and set the result in
- CH.
- }
-
- @NowDoY:
- XOR DH,DH { Make DX the height of image }
- MOV DL,[ES:DI+1]
- MOV CH,DL { Set CH also to height for main blit routine }
-
- CMP BH,$80 { Quick test if Y position is negative }
- JB @YNotNegative
-
- NEG BX { Make Y a positive number }
-
- CMP BX,DX { If Y > ClipHeight }
- JA @NoDraw
- SUB DX,BX { Dec(ClipHeight, Abs(Y) ) }
- MOV CH,DL { As an image can only be 255 bytes high
- this works fine.. }
- PUSH AX { Save X Coord on stack }
- XOR AH,AH
- MOV AL,[ES:DI] { AX = Width }
- MUL BX { Calculate Y * Width }
- ADD SI,AX { Inc(DataStart, Abs(Y) * Width ) }
- POP AX
- XOR BX,BX { Set Y to 0 }
- JMP @NowDoBlit { NOW do the blit work. Whew! }
-
-
-
- @YNotNegative:
- ADD DX,BX { If Y + ClipHeight > 199 Then }
- CMP DX,200
- JB @NowDoBlit
- MOV DX,200
- SUB DX,BX { ClipHeight = 200 - Y }
- MOV CH,DL
-
-
- {
- At this point AX is the X position
- BX is the Y position
- CL is the ClipWidth and
- CH is the ClipHeight.
-
- As the width/height of an Shape can only be an 8 bit
- quantity (i.e. < 256) I can discard the H portions of
- the registers. Whew!
-
- Now follows some weird code.. I'm going to make :
-
- DX = Modulo for datastart (which is the width in bytes of Shape.
- And yes, I do know that Width could be held in DL but adding extra
- code just to satisfy you optimisation junkies is v. boring.)
-
- DS:SI already points to data
- ES:DI points to active (source) Bitmap
-
- }
-
- @NowDoBlit:
- PUSH CX { Save ClipWidth & ClipHeight on stack }
- CALL CalculateOffset { Use AX and BX to calculate screen
- offset. On exit BX is offset }
- POP CX { Restore ClipWidth and ClipHeight }
-
- XOR DH,DH
- MOV DL,[ES:DI] { DX = Modulo }
- MOV DI,BX { Ahhh. Now DI points to the screen offset }
- CLC
- JMP @End
-
- @NoDraw:
- STC { Indicate no blit possible }
-
- @End:
- End;
-
-
-
-
-
-
-
-
-
- {
- This routine does the same as Blit but takes into account
- the fact that the sprite may be off the edges of the
- screen.
-
- Its quite a bit slower than the normal Blit, but that's only
- to be expected as there's more computations to be
- done.
-
- Expects : X, Y specify the horizontal and vertical position of the Shape,
- DataPtr points to the data to blit.
-
- Returns : Nothing
-
- Corrupts : BX,CX,DX,SI,DI,ES.
- }
-
- Procedure ClipBlit(x,y:integer; Var DataPtr); Assembler;
- Asm
- MOV AX,X
- MOV BX,Y
- LES DI,DataPtr
- CALL ClipCalculations
- JC @NoDraw
-
- PUSH DS
- PUSH BP
-
- MOV AX,SourceBitmapSegment
- MOV BX,ES
- MOV DS,BX { Now DS: SI points to correct space }
- MOV ES,AX
-
- MOV BX,SI { BX to be used to reload SI }
- MOV BP,DI { And the screen modulo }
-
- MOV AH,CL { AH = Width }
- CLD { Make sure LODSB works OK }
-
- @Outer:
- MOV CL,AH { Re-load CL }
- MOV SI,BX { And SI with address of next sprite row }
- MOV DI,BP { And DI with address of next scan line }
-
-
- @WriteByte:
- LODSB { Read byte from DS:SI }
- OR AL,AL { Is byte 0 (transparent) ? }
- JZ @NoBlit { yes, so don't blit }
- MOV [ES:DI],AL { Otherwise store byte }
-
- @NoBlit:
- INC DI { Move DI to next pos. on screen }
- DEC CL { Reduce shape width count }
- JNZ @WriteByte { If not zero, end of shape not reached }
-
- ADD BX,DX { BX = BX + Modulo, so BX now points
- to first byte of next sprite line
- to blit }
- ADD BP,320 { Make BP point to next line. Note :
- If you are going to add some extra
- stuff here make sure you're not
- accessing local variables! }
-
-
- DEC CH
- JNZ @Outer
-
- POP BP
- POP DS
- @NoDraw:
- End;
-
-
-
-
-
- {
- This routine does the same as Block except that it takes into account
- that the shape object may be off screen.
-
- Expects : Same as Block.
-
- Returns : Nothing
-
- Corrupts : AX,BX,CX,DX,SI,DI,ES are corrupt on exit.
- }
-
-
-
- Procedure ClipBlock(x,y:integer; Var DataPtr); Assembler;
- Asm
- MOV AX,X
- MOV BX,Y
- LES DI,DataPtr { ES:DI points to data }
- CALL ClipCalculations
- JC @NoDraw
-
-
- {
- Prepare for blit !
- }
-
- PUSH DS
- PUSH BP
-
- MOV AX,SourceBitmapSegment
- MOV BX,ES
- MOV DS,BX { Now DS: SI points to correct space }
- MOV ES,AX
-
- MOV BX,SI { BX to be used to reload SI (+Image Width) }
- MOV BP,DI { And BP to reload DI (+Screen Width) }
-
- CLD { Make sure LODSB works OK }
-
- @Outer:
- PUSH CX
- MOV CH,CL { CH is set to ClipWidth }
-
- MOV SI,BX
- MOV DI,BP
-
- CMP CH,4 { Bytes left < 4 ? }
- JB @CantDoLongWordBlit { Yeah, so can't do the 4 byte blit }
-
- SHR CH,2 { Divide Bytes left by 4 }
-
- @CopyLong:
- DB $66 { Otherwise, store longword to [ES:DI] ! }
- MOVSW
- DEC CH { Reduce long word count }
- JNZ @CopyLong
-
- MOV CH,CL { Restore CL }
- AND CH,3
- OR CH,CH
- JZ @NoBytesLeft
-
-
-
- @CantDoLongWordBlit:
- CMP CH,2 { Byte count < 2 ? }
- JB @CheckDoByteBlit { Yes, can't do a word blit (Shit !)
- so that means that there's only
- 1 byte left. }
- @CopyWord:
- MOVSW { Otherwise, write word }
-
-
- @CheckDoByteBlit:
- TEST CH,1 { Is there a byte left ? }
- JZ @NoBytesLeft { No, so no more blits this line }
-
- @DoByteBlit:
- MOVSB { Store the last byte }
-
- @NoBytesLeft:
- ADD BX,DX { BP to next byte of image to read }
- ADD BP,320 { Advance BX to next scan line }
-
- POP CX
- DEC CH { Reduce Y count }
- JNZ @Outer { if <>0 then go to Outer }
-
- POP BP { Restore base pointer and }
- POP DS { Data Segment }
-
- @NoDraw:
- End;
-
-
-
-
-
-
-
- {
- Grab a rectangular area of bytes from the screen for use
- as a shape object.
-
- Expects : X1,Y1 define the TOP LEFT of the area to grab.
- X2,Y2 define the BOTTOM RIGHT of the area.
-
- X1 MUST be less than X2;
- Similarly, Y1 MUST be less than Y2.
-
- Also, it is NOT possible to grab an image that
- is more than 255 pixels wide and 200 pixels
- high.
-
- Returns : Nothing
-
- Notes : Use the ShapeSize Function to calculate
- bytes needed to hold shape object in memory .
-
- Corrupts : AX,BX,CX,DX,SI,DI,ES
-
- }
-
- Procedure GetAShape(x1,y1,x2,y2:word;Var DataPtr); Assembler;
- Asm
- MOV AX,x1
- MOV BX,y1
- CALL CalculateOffset
- CMP BX,-1
- JZ @StupidUser
-
- MOV AX,x2 { Width = (X2 - X1) +1 }
- SUB AX,x1
- INC AX { Take into account extra pixel }
- MOV DL,AL
-
- MOV AX,y2 { Height = (Y2 - Y1) +1 }
- SUB AX,y1
- INC AX
- MOV DH,AL
-
- LES DI,DataPtr
- MOV [ES:DI],DX { Store Width & Height }
- ADD DI,2
-
- PUSH DS
- MOV DS,SourceBitmapSegment
- CLD { Make sure writes are descending }
-
-
- @Outer:
- MOV SI,BX { SI = Offset into VGA screen }
- MOV CL,DL { CL = Width of sprite held in DL }
-
- CMP CL,4 { Bytes left < 4 ? }
- JB @CantDoLongWordBlit { Yeah, so can't do the 4 byte blit }
-
- SHR CL,2 { Divide Count by 4 }
-
- @CopyLong:
- DB $66 { Otherwise, store longword to [ES:EDI] ! }
- MOVSW
- DEC CL { CL is long word count }
- JNZ @CopyLong { If CL <> 0 go back to CopyLong }
-
- MOV CL,DL { Restore CL to width of Shape }
-
- @CantDoLongWordBlit:
- AND CL,3
- OR CL,CL { Any bytes left ? }
- JZ @NoBytesLeft
-
- CMP CL,2 { Byte count < 2 ? }
- JB @DoByteBlit { Yes, can't do a word blit (Shit !)
- so that means that there's only
- 1 byte left. }
-
- @CopyWord:
- MOVSW { Otherwise, write word }
- TEST CL,1
- JZ @NoBytesLeft { No, so no more blits this line }
-
-
- @DoByteBlit:
- MOVSB { Store the last byte }
-
- @NoBytesLeft:
- ADD BX,320 { Advance BX to next scan line }
- DEC DH { Reduce Y count }
- JNZ @Outer { if <>0 then go to Outer }
-
- POP DS { Otherwise, restore Data Segment }
-
- @StupidUser:
- End;
-
-
-
-
-
-
-
-
-
- {
- This routine checks if the data contained within a Shape will
- "Collide" with the background. (Background data is held within
- the Source Bitmap)
-
- This command is very useful for games that need accurate
- Shape to background collision detection.
-
- Expects : X and Y specify the horizontal and vertical position
- of a shape pointed to by DataPtr.
-
- Returns : If the Shape has collided with ANY background (represented
- by colours 1-255) on the SOURCE Bitmap then BlitColl is TRUE.
-
- Corrupts : AX,BX,CX,DX,SI,DI,ES
- }
-
- Function BlitColl(x,y :integer; Var dataptr) : boolean; Assembler;
- Asm
- MOV AX,x
- MOV BX,y
- CALL CalculateOffset { On exit, BX will hold screen "Offset" }
-
- MOV ES,SourceBitmapSegment
-
- PUSH DS
- PUSH BP
- LDS SI,DataPtr
-
-
- MOV DX,[SI] { DL= Width, DH = Height }
- INC SI
- INC SI { Make SI point to sprite data }
-
- CLD { Make sure writes are descending }
-
- MOV CL,DL
-
- @Outer:
- MOV DI,BX { DI = Offset into Source Bitmap }
- MOV DL,CL
-
- { Check if any long words can be checked }
-
- CMP DL,4 { Is width at least 4 bytes ? }
- JB @CantCheckLong { No }
- SHR DL,2 { Otherwise, divide width by 4 so that
- DL will hold number of LONGs to check }
-
-
- @CheckLong:
- DB $66; LODSW { LODSD : Load EAX from DS:SI }
- DB $66; OR AX,AX { OR EAX,EAX }
- JZ @NoCheckBackLong { If EAX is zero then no point in checking
- background is there ? }
-
- DB $66
- MOV BP,AX { Make a copy of EAX }
- DB $66
- XOR AX,[ES:DI] { XOR EAX, [ES:DI] (Xor EAX with Background) }
- DB $66
- CMP BP,AX { Is EAX unaffected by the XOR - i.e.
- No collision }
- JNZ @CollisionOccurred
-
-
- @NoCheckBackLong:
- ADD DI,4 { Bump DI to next long word }
- DEC DL { Reduce long word count }
- JNZ @CheckLong { And now do the collision check for long word }
-
-
-
- MOV DL,CL { Restore DL to it's previous contents }
- AND DL,3 { Mask out all but bits 0 & 1 }
-
-
- { Any words left to be checked ? }
-
- @CantCheckLong:
- CMP DL,2 { Is there at least 2 bytes left to move ? }
- JB @CantCheckWord { No }
-
- @CheckWord:
- LODSW { Read word from DS:SI into AX }
- OR AX,AX { Is Shape data non zero ? }
- JZ @CantCheckWord { Yes, so can't be a collision }
-
- MOV BP,AX
- XOR AX,[ES:DI] { Otherwise, check background too }
- CMP BP,AX { Is AX different ? }
- JNZ @CollisionOccurred { Yes, so this means a collision }
- ADD DI,2 { Otherwise add 1 byte }
-
- @CantCheckWord:
- TEST CL,1 { Is there a single byte left to check }
- JZ @AllChecksDone { Nope }
- LODSB { Otherwise, read it }
- OR AL,AL { Zero ? }
- JZ @AllChecksDone { Yes, so basically no more checks to do }
-
- MOV CH,AL
- XOR AL,[ES:DI] { No, so check background byte }
- CMP CH,AL { Is AL different ? }
- JNZ @CollisionOccurred { Yes, so a collision has occurred }
-
-
- @AllChecksDone:
- ADD BX,320 { 320 is the number of bytes in one scan-line }
- DEC DH { Reduce vertical count (Counts from height of Shape) }
- JNZ @Outer { If <>0 then check for next line of Shape }
- MOV AL,False { If all lines have been done then this means
- that no collision has occurred }
-
- JMP @Exit { And exit. Don't insert a RET here -
- you'll crash the program ! }
-
- @CollisionOccurred:
- MOV AL,True { This part is only reached if a collision has
- occurred. }
-
- @Exit:
- POP BP { Restore Base Pointer }
- POP DS { Restore data segment }
- End;
-
-
-
-
-
-
- {
- De-allocate memory for an Shape.
-
- Expects : DataPtr is an Shape pointer.
-
- Returns : A crash if you're not careful !! :-(
-
- Corrupts : The assembler part uses AX,DI and ES. Don't know about
- the Pascal part however.
- }
-
- Procedure FreeShape(DataPtr:pointer);
- Var ImWidth,
- ImHeight: byte;
- Begin
- Asm
- LES DI,DataPtr
- MOV AX,[ES:DI]
- MOV ImWidth,AL
- MOV ImHeight,AH
- End;
- FreeMem(DataPtr,ExtShapeSize(ImWidth,ImHeight));
- End;
-
-
-
-
-
-
- {
- Load in a .IMG file from disk.
-
- WARNING! This is NOT the IMG file type used by some paint packages!
- It is a non-standard file (albeit very simple) format that NEWGRAPH
- writes, so trying to load a shape created from a paint package
- etc. will not work.
-
-
- Expects : FileName to be a valid MS-DOS path.
- DataPtr to be a valid pointer to where data will be stored.
-
- Returns : Nothing, although sprite may have loaded into memory.
-
- Corrupts : Don't know.
-
- }
-
- Procedure LoadShape(FileName:String; Var DataPtr: Pointer);
- Var F: File;
- DestSeg,
- DestOffset,
- ImgSize: word;
- ShapeWidth,
- ShapeHeight: byte;
-
- Begin
- Assign(F,FileName);
- Reset(F,1);
- BlockRead(F,ShapeWidth,1); { Read in width & height }
- BlockRead(F,ShapeHeight,1);
-
-
- {
- Calculate number of bytes that need to be reserved for the
- Shape.
- }
-
- ImgSize:= ExtShapeSize(ShapeWidth,ShapeHeight);
- If ImgSize < MaxAvail Then
- Begin
-
- GetMem(DataPtr,ImgSize);
- GetPtrData(DataPtr,DestSeg,DestOffset);
-
- Reset(F,1);
- BlockRead(F,Mem[DestSeg:DestOffset], ImgSize);
- Close(F);
- End
- Else
- Asm
- DB $66
- MOV WORD [OFFSET DataPtr],0 { Signal no memory claimed }
- DW 0
- End;
-
- End;
-
-
-
-
-
-
-
-
- {
- Write an Shape to disk, where you could convert it if you like
- to a PCX. With the reg. version, there is a command to do this.
-
- Expects : FileName is a standard DOS filename.
- P is a pointer to where the sprite data exists in memory.
-
- Returns : Nothing.
-
- Corrupts : Don't know.
- }
-
- Procedure SaveShape(FileName:string; DataPtr:Pointer);
- Var F: File;
- SourceSeg, SourceOffset: word;
- Begin
- Assign(F,FileName);
- Rewrite(F,1);
- GetPtrData(DataPtr,SourceSeg,SourceOffset);
-
- BlockWrite(F, Mem[SourceSeg:SourceOffset],
- ExtShapeSize(mem[SourceSeg:SourceOffset],
- mem[SourceSeg:SourceOffset+1]));
- Close(F);
- End;
-
-
-
-
-
-
-
-
-
- {
- ***************************************
- PCX LOAD AND SAVE ROUTINES - WHICH WORK
- ***************************************
- }
-
-
-
- {
- This will put a mode 13h 256 colour PCX at position X,Y and
- show a defined area. Useful for low res multimedia applications. :-)
- This PCX loader can handle PCX's of variable dimensions up to
- width 320 and height 200 so you could design sprites
- with a graphics package and save them as a PCX then grab them
- off the screen as Shapes. Also, this PCX loader is far faster than
- Norman Yen's effort and intelligently uses memory. (Note: How can
- a program dumbly use memory? Hmm?)
-
- Expects: Filename is an MS-DOS filespec relating to the PCX's name,
- i.e. 'C:\WORK\SHEEP.PCX' (Oh well explained Scott :^( )
- ThePalette is a PaletteType record used to hold the PCX's
- palette data.
- X,Y specifies the top left coordinates on screen of where
- the PCX is to be drawn. X should be in the range of 0 to
- 319, Y should be in the range of 0 to 199. The picture
- will be clipped as necessary.
-
- Returns: Your program will halt with an error message if the PCX file
- does not exist, or if the PCX is not of the correct "type".
- (I.E. It's not mode 13h or it's not 256 colour etc.).
- }
-
- Procedure LocatePCX(filename:string; Var ThePalette: PaletteType;
- x,y,widthtoshow,heighttoshow:word);
-
- var PCXFile: file;
-
- ReadingFromMem : Boolean; { If True it means All/Some PCX
- Data is in RAM }
- MemRequired : longint; { Size of PCX bitmap data }
- BytesRead : longint; { Number of PCX bytes read }
- PCXFileSize : longint; { How many bytes PCX uses }
- Count : integer; { I is a general counter used to set
- the PCX's palette and then count
- scan lines }
- RedVal : byte; { Used for ColourMap, Palette values }
- GreenVal : byte; { which define a colour }
- BlueVal : byte;
-
- MemoryAccessVar : pointer; { Pointer to read bitmap data }
- BufferSeg, { Where PCX will be loaded to }
- BufferOffset : word;
-
- VidOffset : word; { Screen offset }
-
- Width,Height, { Width is number of horizontal bytes to grab
- Height is number of vertical bytes to grab }
- N,Bytes : word; { N counts up to Bytes }
- RunLength,c : byte; { RunLength is the Run Length Encoding
- byte, C is the character read from
- PCX data }
- PastHorizontalLimit : boolean; { Set true this means no more
- horizontal pixel writes to do, advance
- to next line as soon as poss.}
-
- begin
- assign(PCXFile,FileName);
-
- {$i-}
- reset (PCXFile,1);
- {$i+}
- If IOResult = 0 Then
- Begin
-
- blockread (PCXFile, header, sizeof (header)); { Read in PCX header }
-
- if (header.manufacturer=10) and (header.version=5) and
- (header.bits_per_pixel=8) and (header.colour_planes=1) then
- begin
- seek (PCXFile, filesize (PCXFile)-769); { Move to palette data }
- blockread (PCXFile, c, 1); { Read Colourmap type }
- if (c=12) then { 12 is correct type }
- begin
- {
- Read palette data and write to palette
- structure.
- }
-
- for Count:=0 to 255 do
- Begin
- BlockRead(PCXFile,RedVal,1);
- BlockRead(PCXFile,GreenVal,1);
- BlockRead(PCXFile,BlueVal,1);
-
- ThePalette.RedLevel[Count]:=RedVal SHR 2;
- ThePalette.GreenLevel[Count]:=GreenVal SHR 2;
- ThePalette.BlueLevel[Count]:=BlueVal SHR 2;
- End;
-
-
- seek (PCXFile, 128);
-
- {
- If entire size of PCX is less than 64K in length then
- it can be stored in a memory buffer and uncompacted
- from there. However, if PCX exceeds 64K then it must
- be split into several chunks. If your machine does
- not have 64K left for the buffer used (You're in trouble !!)
- then the system will read the PCX from disk continually,
- which works OK but is very slow. So there.
- }
-
- MemRequired:=Filesize(PCXFile)-897;
- PCXFileSize:=MemRequired;
- BytesRead:=0;
-
- If (MemRequired < 65528) And (MaxAvail > MemRequired) Then
- Begin
- getmem(MemoryAccessVar,MemRequired);
- GetPtrData(MemoryAccessVar, BufferSeg, BufferOffset);
- BlockRead(PCXFile,Mem[BufferSeg:BufferOffset],MemRequired);
- ReadingFromMem:=True;
- End
- Else
-
- {
- If the PCX occupies more than approx. 64K bytes then it
- is necessary to read the data into memory in 64K chunks
- which is still considerably faster than the
- final method (continual reading from disk)
- }
-
- If (MaxAvail > 65527) Then
- Begin
- GetMem(MemoryAccessVar,65528);
- GetPtrData(MemoryAccessVar, BufferSeg, BufferOffset);
- BlockRead(PCXFile,Mem[BufferSeg:BufferOffset],65528);
- BytesRead:=65528;
- MemRequired:=65528;
- ReadingFromMem:=True;
- End
- Else
- { CLUCK!! Oh well, system is just going to have
- to read from disk as there is not even 64K
- memory left. (A very bad situation) }
-
- ReadingFromMem:=False;
-
- {
- Find out width & height of PCX.
- }
-
- width:=(header.xmax - header.xmin)+1;
- height:=(header.ymax - header.ymin)+1;
- bytes:=header.bytes_per_line;
-
- {
- Adjust width & height of PCX if necessary so that PCX
- "fits" on screen.
-
- }
-
- if widthtoshow > width Then
- widthtoshow:=width;
-
- if (widthtoshow + x) > 320 Then
- widthtoshow:=width-x;
-
- if heighttoshow > height Then
- heighttoshow:=height;
-
- if (heighttoshow + y)> 200 Then
- heighttoshow:=height-y;
-
-
- {
- Do all scan lines.
- }
-
- for Count:=0 to (heighttoshow-1) do
- begin
- n:=0;
- PastHorizontalLimit:=False;
- vidoffset:= SourceBitmapOffset+((Y+Count)* 320)+X;
-
- while (n<bytes) do
- begin
-
- { Display any more pixels width wise from PCX ? }
-
- If N >= WidthToShow Then
- PastHorizontalLimit:=True;
-
- If ReadingFromMem Then
- Begin
- c:=Mem[BufferSeg:BufferOffset];
- Inc(BufferOffset);
- If BufferOffset = 65528 Then
- Begin
- { End of buffer has been reached, so
- it's time to load another part of the
- PCX }
-
- If (PCXFileSize - BytesRead)> 65527 Then
- Begin
- BlockRead(PCXFile,Mem[BufferSeg:0],65528);
- Inc(BytesRead,65528);
- End
- Else
- { Load last chunk of PCX }
-
- Begin
- BlockRead(PCXFile,Mem[BufferSeg:0],
- (PCXFileSize - BytesRead));
- End;
-
- {
- Now reset buffer pointer to start
- }
-
- BufferOffset:=0;
- End;
- End
- Else
- BlockRead(PCXFile,c,1);
-
- {
- At this point one element of data has been read, and stored in
- variable C. If bits 6 & 7 of C are set then this means to the system
- a "run of bytes" has been found. (i.e. a number sequence - for example,
- four 1's, twenty 15's, any sequence of identical numbers).
-
- In this case, the 6 least significant bits of C indicate how long the run
- of bytes is. For example, if a sequence of five bytes has been found
- the run = 5. Of course, using 6 bits limits you to a maximum run length
- of 63 bytes but that should be more than enough for most pictures.
-
- Quite a simple method of compaction eh? Definitely the easiest format to
- understand!
-
- }
-
- if ((c and 192)=192) then
- begin
-
- { Get the 6 least significant bits }
- RunLength:=c and 63;
-
- { get the run byte }
-
- If ReadingFromMem Then
- Begin
- c:=Mem[BufferSeg:BufferOffset];
- Inc(BufferOffset);
-
- { Time to read in more data from disk ? }
-
- If BufferOffset = 65528 Then
- Begin
- If (PCXFileSize - BytesRead)> 65527 Then
- Begin
- BlockRead(PCXFile,Mem[BufferSeg:0],65528);
- Inc(BytesRead,65528);
- End
- Else
- Begin
- BlockRead(PCXFile,Mem[BufferSeg:0],
- (PCXFileSize - BytesRead));
- End;
-
- BufferOffset:=0;
- End;
- End
- Else
- BlockRead(PCXFile,c,1);
-
- {
- Can't do blit if past the horizontal limit
- of the window.
- }
-
- If Not PastHorizontalLimit Then
- Begin
- If n+RunLength > widthtoshow Then
- fillchar(Mem[SourceBitmapSegment:VidOffset],WidthToShow-n,c)
- else
- fillchar(Mem[SourceBitmapSegment:VidOffset],RunLength,c);
-
- inc(vidoffset,RunLength);
- End;
-
- inc(n,RunLength);
- end
- else
- begin
- If Not PastHorizontalLimit Then
- Begin
- mem [SourceBitmapSegment:vidoffset]:=c;
- inc (vidoffset);
- End;
- inc (n);
- end;
-
- end;
-
- end;
-
- If ReadingFromMem Then
- freemem(MemoryAccessVar,MemRequired);
- end
- else
- Begin
- DirectVideo:=False;
- Writeln('The PCX''s ColourMap is not of the correct type !');
- Close(PCXFile);
- Halt(0);
- End;
- end
- Else
- Begin
- DirectVideo:=False;
- Writeln('PCX unsuitable for loading.');
- Close(PCXFile);
- Halt(0);
- End;
-
- close (PCXFile); { Do this anyway ! }
-
- end
- Else
- Begin
- DirectVideo:=False;
- Writeln('File not found ?');
- Close(PCXFile);
- Halt(0);
- End;
-
- end;
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- What this does is load a PCX at the TOP LEFT of the source Bitmap,
- very quickly. If you need to put the PCX somewhere else use LocatePCX.
-
-
- Expects: FileName to be a standard MS-DOS filename, relating to a
- 320 x 200 PCX.
- ThePalette to be of type Palette. This holds the colour
- information of the PCX file you are loading.
-
- You can then use SetAllPalette to set the VGA palette so that
- the pic can display properly.
- }
-
- Procedure LoadPCX(FileName:string; Var ThePalette: PaletteType);
- Begin
- LocatePCX(Filename,ThePalette,0,0,320,200);
- End;
-
-
-
-
-
-
- {
- Home grown PCX packer.
-
- This PCX routine is able to cope with the full 256 colours,
- unlike some other SWAG PCX packers I could mention.. !
-
- Expects: FileName is the name of the PCX to save.
- ThePalette is a PaletteType variable, which has been
- initialised by, for example, the GetAllPalette routine.
- X,Y specify the horizontal and vertical positions of where to
- begin grabbing the PCX data from.
- PCXWidth and PCXHeight specify the width & height of the
- window to grab. Easy eh?
-
- For example, to grab one half of the VGA screen you could use:
- SaveAreaAsPCX('1STHALF.PCX',MyPalette,0,0,160,200);
-
- And the other half with :
-
- SaveAreaAsPCX('2NDHALF.PCX',MyPalette,160,0,160,200);
-
- These files can then be loaded into a paint package such
- as PC Paintbrush or Neopaint (great program!) and manipulated.
-
- Use the SAVEPCX routine below to save an entire PCX screen.
-
-
- Returns: Program will halt if the PCX is not found.
-
-
- P.S. This routine manages to save a 256 colour screen properly,
- unlike some other PCX writing routines I could mention. Do you
- programmers actually TEST your code before sending it into the
- SWAG ? (Like, are there any GIF loaders that work ?!!)
- }
-
-
- Procedure SaveAreaAsPCX(filename:string;ThePalette: PaletteType;
- x,y, PCXWidth,PCXHeight: word);
-
- Var f: File; { File for writing PCX to }
- ColourMapID: byte; { Always holds 12, for the PCX }
- ColourCount: byte; { Counts up to number of colours on
- screen (255) }
- RedValue: byte; { Palette Values of a colour }
- GreenValue: byte;
- BlueValue: byte;
-
- LastOffset: word; { Used as a latch for VidOffset }
- VidOffset: word; { Offset into Source Bitmap }
- VerticalCount: byte; { Number of scan lines to use }
- LastByte : byte; { The last byte read from Source Bitmap }
- NewByte: byte; { The current byte }
- RunLength : byte; { Counter for run length compression }
- ByteCount: word; { Counts up to bytes per scan line (320) }
-
-
-
- Begin
- Assign(f,filename);
- Rewrite(f,1);
-
- With header do
- Begin
- Manufacturer := 10;
- Version := 5;
- Encoding :=0;
- Bits_per_pixel:=8; { 8 bits = 256 colours }
- XMin:=0;
- YMin:=0;
-
- {
- Can't save a PCX more than 320 x 200 in size.
- }
-
- if (PCXwidth + x) > 320 Then
- PCXwidth:=320-x;
- if (PCXheight+ y) > 200 Then
- PCXheight:=200-y;
-
- XMax:=(PCXWidth-1);
- YMax:=(PCXHeight-1);
- Hres:=320; { Hres/Vres could be used to
- determine screen mode -
- probably :-( }
- VRes:=200;
-
- Colour_planes:=1; { Mode 13h is not planar }
- Bytes_per_line:=PCXWidth; { One byte per pixel }
- Palette_type:=12; { Dunno what 12 is for }
- End;
-
- BlockWrite(F,Header,SizeOf(Header));
-
- Asm
- MOV AX,X
- MOV BX,Y
- CALL CalculateOffset
- MOV VidOffset,BX
- End;
-
- For VerticalCount:=0 to PCXHeight-1 do
- Begin
- LastOffset:=VidOffset;
- ByteCount:=0;
- LastByte:=0;
-
- Repeat
- NewByte:=Mem[SourceBitmapSegment:Vidoffset];
-
- {
- If the last byte read is equal to the new byte read
- then a run of bytes has been identified and so the
- system needs to count how many identical bytes (up
- to a total of 63) follow. When finished, the
- system writes this count to disk PLUS a value of
- 192 (which is the signal to the PCX reader that
- a run of bytes follows) then writes the byte that
- was prevalent in the run.
-
- For example, say in the data stream there were 10
- values :
-
- 0 1 2 6 9 8 7 7 7 4
-
- When the system gets to 8 it would then compare
- that number with the next value (7) and see that 8 is
- not equal to 7, then the computer would move to said 7
- (after the 8) and compare it to the next digit, which
- is also a 7.
-
- As a match has been found, the system counts the
- number of 7s there, which is (all together now !)
- 3!! and then adds 192 to the result.. to give 195.
-
- As stated before, bits 6 + 7 of the byte have
- been set in order to "flag" to the PCX reader that
- a run of bytes have been found.
-
- The value 195 is written to disk, then value 7 so the
- PCX reader that loads this file knows what value (and
- how many times) to write to the screen during unpacking.
-
- I hope this has explained one of the PCX mysteries. If
- it hasn't I typed all that for nothing!! :-)
- }
-
- If NewByte = LastByte Then
- Begin
-
- RunLength:=0;
- While (NewByte = LastByte) and (RunLength < 63)
- and (ByteCount <> PCXWidth) do
- Begin
- Inc(RunLength);
- Inc(ByteCount);
-
- {
- Move to next byte on Source Bitmap
- }
-
- Inc(vidoffset);
-
- NewByte:=Mem[SourceBitmapSegment:Vidoffset];
- End;
-
-
- Asm
- OR Byte Ptr RunLength, 192
- End;
-
- BlockWrite(f,RunLength,1);
- BlockWrite(f,LastByte,1);
-
- LastByte:=NewByte;
- End
- Else
-
- { How to deal with colours > 191. }
- If (NewByte > 191) Then
- Begin
- Inc(ByteCount);
- Inc(VidOffset); { Point to next byte on screen }
- RunLength:=193;
- BlockWrite(f,RunLength,1); { Write run length byte of 1 ! }
- BlockWrite(f,NewByte,1); { The ONLY way to get round }
- LastByte:=NewByte;
- End
- Else
- Begin
- Inc(ByteCount);
- Inc(vidoffset);
- BlockWrite(f,NewByte,1);
- LastByte:=NewByte;
- End;
-
- Until ByteCount = PCXWidth;
-
- VidOffset:=LastOffset+320;
- End;
-
- {
- 12 is Colourmap ID.
- }
-
- ColourMapID:=12;
- BlockWrite(f,ColourMapID,1);
-
- {
- Now write Palette R,G,B values to disk. The only reason
- I didn't implement :
-
- BlockWrite(F,Palette,SizeOf(Palette))
-
- was that all the palette entries had to be shifted LEFT
- twice (To represent a 16.7 million colour palette..)
-
- DAMN!
- }
-
- For ColourCount:=0 to 255 do
- Begin
-
- RedValue:=ThePalette. RedLevel[ColourCount] SHL 2;
- GreenValue:=ThePalette. GreenLevel[ColourCount] SHL 2;
- BlueValue:=ThePalette. BlueLevel[ColourCount] SHL 2;
-
- BlockWrite(F,RedValue,1);
- BlockWrite(F,GreenValue,1);
- BlockWrite(F,BlueValue,1);
- End;
-
- Close(F); { That's it - it's not over, not over yet .. :-) }
- End;
-
-
-
-
-
-
- {
- Save a PCX file to disk.
-
- Expects : Filename is the MS-DOS filespec , i.e. "C:\PICS\MYFILE.PCX"
- ThePalette specifies a PaletteType record to save to disk in
- the PCX file.
-
- Returns : Nothing
-
- Corrupts : Don't know !!
- }
-
-
- Procedure SavePCX(filename:string;ThePalette: PaletteType);
- Begin
- SaveAreaAsPCX(filename,ThePalette,0,0,320,200);
- End;
-
-
-
-
-
- {
- **********************
- MISCELLANEOUS ROUTINES
- **********************
- }
-
- {
- Wait for a certain number of vertical retraces, specified by
- the number in TimeOut. (A vertical retrace occurs when the
- monitor begins to draw the screen; If you wait for this
- retrace and then update the screen your graphics will not
- flicker - well not as much as before ;-) )
-
-
- Corrupts AL,CX,DX
- }
-
-
- Procedure Vwait(TimeOut:word); Assembler;
- Asm
- MOV CX,TimeOut { CX = Number of times to wait }
-
- MOV DX,$3DA { Port $3DA holds vertical & horizontal
- retrace status bits }
- @WaitEnd:
- IN AL,DX { Read port }
- TEST AL,8 { Test for bit 3 being set: If
- it is then that means the system may
- be in the middle of it's refresh
- and so writing to the screen now may
- cause flicker. }
- JNZ @WaitEnd { If set, go back to @waitend }
-
- {
- When the routine gets to here it's the end of the retrace.
- }
-
- @WaitStart:
- IN AL,DX { Read port again }
- TEST AL,8 { Is the bit set ? }
- JZ @WaitStart { No ! So go back }
-
- DEC CX
- JNZ @WaitEnd { Reduce count in CX, if <>0 go back
- to WaitEnd ! }
-
- End;
-
-
-
-
-
-
- {
- Clear the Source Bitmap with Colour 0 (Always).
-
- Expects : SourceBitmapSegment, SourceBitmapOffset to point to the source
- Bitmap (of course).
-
- Returns : Nothing.
-
- Corrupts : AX,CX,DI,ES
-
-
- }
-
- Procedure Cls; Assembler;
- Asm
- MOV ES,SourceBitmapSegment
- MOV DI,SourceBitmapOffset
-
- MOV CX,4000 { 4000 x 16 byte moves are executed }
- DB $66
- XOR AX,AX { XOR EAX,EAX - Colour 0 used to clear screen }
-
- @ClearLoop:
- DB $66; STOSW { STOSD }
- DB $66; STOSW
- DB $66; STOSW
- DB $66; STOSW
- DEC CX
- JNZ @ClearLoop
-
- End;
-
-
-
-
-
- {
- Clear the screen with the graphics colour specified.
-
- Expects : CurrentColour set to non-zero value
- Source Bitmap initialised with Bitmap
-
- Returns : Nothing
-
- Corrupts : AX,BX,CX,DI,ES
- }
-
- Procedure CCls(TheColour : byte); Assembler;
- Asm
- MOV ES,SourceBitmapSegment
- MOV DI,SourceBitmapOffset
-
- MOV CX,4000
- MOV AH,TheColour
- MOV AL,AH
- MOV BX,AX
-
- DB $66; SHL AX,16 { SHL EAX,16 -> Move AH & AL into
- upper word of EAX}
- MOV AX,BX { Now EAX is fully set }
-
- @FillLoop:
- DB $66; STOSW { STOSD }
- DB $66; STOSW
- DB $66; STOSW
- DB $66; STOSW
- DEC CX
- JNZ @FillLoop { You could use LOOP but I heard this
- method is faster }
- End;
-
-
-
-
-
- {
- This is the initialisation part of the unit.
- }
-
- Begin
- SetSourceBitmapAddr($a000,0);
- DoubleBufferOff;
- Cls; { Flush video mem }
- MoveTo(0,0); { Graphics Cursor to top left }
- SetColour(1); { Use Colour 1 }
- UseFont(Font8x8); { standard 8 x 8 }
-
- Writeln('NewGraph unit (C) 1995, 1996 Scott Tunstall. All rights');
- Writeln('reserved. Unauthorised editing/duplication of this code');
- Writeln('is PROHIBITED.');
- Writeln;
-
- End. { of unit }